package com.captjack.shiro.component;

import com.captjack.shiro.entity.SysPermission;
import com.captjack.shiro.entity.SysRole;
import com.captjack.shiro.entity.SysUser;
import com.captjack.shiro.service.SysPermissionService;
import com.captjack.shiro.service.SysRoleService;
import com.captjack.shiro.service.SysUserService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.List;

/**
 * 自定义权限匹配和账号密码匹配
 *
 * @author Capt Jack
 * @date 2018/6/7
 */
@Component
public class CustomizeShiroRealm extends AuthorizingRealm {

    /**
     *
     */
    private final SysUserService sysUserService;

    /**
     *
     */
    private final SysRoleService sysRoleService;

    /**
     *
     */
    private final SysPermissionService sysPermissionService;

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        // 因为非正常退出，即没有显式调用 SecurityUtils.getSubject().logout()
        // (可能是关闭浏览器，或超时)，但此时缓存依旧存在(principals)，所以会自己跑到授权方法里。
        if (!SecurityUtils.getSubject().isAuthenticated()) {
            doClearCache(principalCollection);
            // 退出登录
            SecurityUtils.getSubject().logout();
            return null;
        }
        if (principalCollection == null) {
            throw new AuthorizationException("parameters principals is null");
        }
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        // 获取用户信息
        SysUser sysUser = (SysUser) principalCollection.getPrimaryPrincipal();
        try {
            // 获取用户的所有角色
            List<SysRole> roles = sysRoleService.findSysRoleByUserId(sysUser.getUid());
            for (SysRole role : roles) {
                // 添加角色
                authorizationInfo.addRole(role.getRole());
                // 获取该角色的所有操作权限
                List<SysPermission> sysPermissions = sysPermissionService.findSysPermissionByRoleId(role.getId());
                // 赋权
                for (SysPermission perm : sysPermissions) {
                    authorizationInfo.addStringPermission(perm.getPermission());
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return authorizationInfo;
    }

    /**
     * 主要是用来进行身份认证的，也就是说验证用户输入的账号和密码是否正确。
     *
     * @param authenticationToken 用户信息
     * @return 鉴权信息
     * @throws AuthenticationException 异常
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        // 获取用户的输入的账号.
        String username = (String) authenticationToken.getPrincipal();
        // 通过username从数据库中查找 User对象，如果找到，没找到.
        // 实际项目中，这里可以根据实际情况做缓存，如果不做，Shiro自己也是有时间间隔机制，2分钟内不会重复执行该方法
        SysUser sysUser = sysUserService.findUserByUsername(username);
        if (sysUser == null) {
            return null;
        }
        // 账户冻结
        if (sysUser.getState() != null && sysUser.getState() == 1) {
            throw new LockedAccountException();
        }
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
                // 用户名
                sysUser,
                // 密码
                sysUser.getPassword(),
                // salt = username + salt
                ByteSource.Util.bytes(sysUser.getCredentialsSalt()),
                // realm name
                getName()
        );
        return authenticationInfo;
    }

    @Autowired
    public CustomizeShiroRealm(SysUserService sysUserService, SysRoleService sysRoleService, SysPermissionService sysPermissionService) {
        this.sysUserService = sysUserService;
        this.sysRoleService = sysRoleService;
        this.sysPermissionService = sysPermissionService;
    }

}